package com.foreveross.pushsdk.core;

import com.foreveross.pushsdk.main.PushManager;

import org.json.JSONObject;
import org.msgpack.MessagePack;
import org.msgpack.type.Value;

import java.io.IOException;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * description:只负责socket通讯,处理收发
 * project:
 * author: huang li
 * date: 2014/5/13
 */
public class PushSocket {


    private  String host;
    private  int port;
    /**
     * 消息回执和拉取消息的地址
     */
    private  String msgHost;
    /**
     * 消息回执和拉取消息的端口
     */
    private int msgPort;

    private Channel channel;
    private EventLoopGroup group;

    private PushManager manager;

    public PushSocket(String host, int port,PushManager manager) {
        this.host = host;
        this.port = port;
        this.manager=manager;
    }

    /**
     * 进行连接，该方法会block
     */
    public void connectInternal() {
        Bootstrap bootstrap = new Bootstrap();
        group=new NioEventLoopGroup();
        bootstrap.group(group);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.handler(new PushSocketChannelInitializer());

        try {
            channel = bootstrap.connect(host, port).sync().channel();
            channel.closeFuture().sync();
        } catch(InterruptedException e) {
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
            manager.connect();
        }finally{
            if(group!=null) group.shutdownGracefully();
        }
    }

    /**
     * 判断socket连接是否仍然有效
     *
     * @return true 表示可以进行收发通讯,false的话,需要手动关闭socket
     */
    public boolean isSocketAvailable() {

        boolean isActive = (channel != null && channel.isOpen());
        return isActive;
    }

    public void disConnect() {
        if(channel != null) {
            channel.closeFuture();
            channel = null;
        }
        if(group != null) {
            group.shutdownGracefully();
            group = null;
        }
    }

    /**
     * 发送消息
     *
     * @param msg 要发送的消息
     * @return 消息是否被正常发送，null 或者 状态不正常时，不能发送消息
     */
    public boolean sendMsg(byte[] msg) {
        if(msg == null) {
            return false;
        }
        if(isSocketAvailable()) {
            ByteBuf buf = channel.alloc().buffer(msg.length);
            buf.writeBytes(msg);
            channel.writeAndFlush(buf);
            return true;
        } else {
            return false;
        }
    }
    /**
     * 这里处理，接收数据、连接成功后的回调
     */
    private class PushSocketChannelInitializer extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            pipeline.addLast(new SimpleChannelInboundHandler<Object>() {
                //接收到数据
                @Override
                protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
                    ByteBuf buf = (ByteBuf) msg;
                    byte[] bytes = new byte[buf.readableBytes()];
                    buf.readBytes(bytes);
                    if (isHeartbeat(bytes)) {
                        HeartbeatReciverList.modif();
                        manager.setTryTimesToZero();
                    } else {
                        Value v= PackageUtil.byteToObject(bytes);
                        JSONObject jsonObject=new JSONObject(v.toString());
                        if(!jsonObject.isNull("result")) {
                            boolean r = jsonObject.getBoolean("result");
                            if (r) {
                                msgHost=jsonObject.getString("host");
                                msgPort=jsonObject.getInt("port");
                                manager.socketAuthed();
                            } else {
                                manager.socketAuthError();
                            }
                        }else {
                            if (!jsonObject.isNull("offline")){
                                String r=manager.getOfflineMessage(msgHost,msgPort);
                                if(r==null)return;
                                JSONObject j=new JSONObject(r);
                                if (j.getBoolean("result")){
                                    manager.messageReceiver.receive(j.getString("message"));
                                }
                            }else {
                                if (jsonObject.getBoolean("isWrap")) {
                                    manager.messageFeedback(jsonObject.getString("id"), msgHost, msgPort);
                                    manager.messageReceiver.receive(jsonObject.toString());
                                } else {
                                   String r= manager.getPushMessage(jsonObject.getString("id"), msgHost, msgPort);
                                    if(r!=null) {
                                        manager.messageReceiver.receive(r);
                                    }
                                }
                            }
                        }
                    }
                    return;
                }

                @Override
                public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                    cause.printStackTrace();
                    super.exceptionCaught(ctx, cause);

                }

                @Override
                public void channelActive(ChannelHandlerContext ctx) {
                    manager.registerHeartBeatReceiver();
                }

            });
        }
    }
    /**
     * 判断是否为心跳回执
     * @param btyes
     * @return
     */
    private boolean isHeartbeat(byte[] btyes){

        byte b;
        try {
            b = new MessagePack().write("")[0];
            byte[] msgHeartbeat={0,0,0,1,1,b};

            if (btyes.length==msgHeartbeat.length) {
                for (int i = 0; i < btyes.length; i++) {
                    if (btyes[i]!=msgHeartbeat[i])return false;
                }
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }

    }

}
